/**
 * \file Code.h
 * \brief Headers for control-oriented Code subclasses
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl> 
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#ifndef __CODE_H_INCLUDED
#define __CODE_H_INCLUDED

#include <iostream>
#include "Type.h"
#include "Base.h"
#include "LeftExpression.h"

class Stat : public Code
{
  public:
    Stat() : Code(&BasicVoid) {}
    Stat(Expression *_data, bool _tentative=false) : Code(&BasicVoid, false, _tentative),
      data(_data) {}

    ~Stat()
    {
      delete data;
    }

    Stat *copy()
    {
      return new Stat(data->copy(), tentative);
    }

    Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);
    void clear(void);

    std::string toString(int indentation=0);
    std::string toTerm();

    Code *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return new Stat(data->replace(src, rplc, symbols, out));
    }

  public:
    Expression* data;
};

class TranslationUnit : public Code
{
  public:
    typedef std::vector<Code*>::iterator TI;

    TranslationUnit() : Code(&BasicVoid) {}

    ~TranslationUnit()
    {
      if (data.empty())
	return;

      for (TI ii=data.begin(); ii != data.end(); ++ii)
	delete (*ii);
    }

    TranslationUnit *copy()
    {
      TranslationUnit *res = new TranslationUnit();

      for (TI ii=data.begin(); ii != data.end(); ++ii)
	res->push_back((*ii)->copy());

      return res;      
    }

    Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);

    std::string toString(int indentation=0);
    std::string toTerm();

    Code *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      TranslationUnit *res = new TranslationUnit();

      for (TI ii=data.begin(); ii != data.end(); ++ii)
	res->push_back((*ii)->replace(src, rplc, symbols, out));

      return res;      
    }

    void push_back(Code *code);

  public:
    std::vector<Code*> data;
};

class Compound : public Code
{
  public:
    typedef std::vector<Code*>::iterator CI;

    Compound(bool _automatic=false) :
      Code(&BasicVoid), automatic(_automatic), parent(NULL) {}

    ~Compound()
    {
      if (!decls.empty())
	for (CI ii=decls.begin(); ii != decls.end(); ++ii)
	  delete (*ii);

      if (!stats.empty())
	for (CI ii=stats.begin(); ii != stats.end(); ++ii)
	  delete (*ii);
    }

    Compound *copy()
    {
      Compound *res = new Compound(automatic);

      if (!decls.empty())
	for (CI ii=decls.begin(); ii != decls.end(); ++ii)
	  res->push_back_decl((*ii)->copy());

      if (!stats.empty())
	for (CI ii=stats.begin(); ii != stats.end(); ++ii)
	  res->push_back_stat((*ii)->copy());

      return res;      
    }

    Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualizeBreaks(void)
    {
      if (!stats.empty())
	for (CI ii=stats.begin(); ii != stats.end(); ++ii)
	  (*ii)->residualizeBreaks();
    }

    std::string toString(int indentation=0);
    std::string toTerm();

    Code *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      Compound *res = new Compound(automatic);

      if (!decls.empty())
	for (CI ii=decls.begin(); ii != decls.end(); ++ii)
	  res->push_back_decl((*ii)->replace(src, rplc, symbols, out));

      if (!stats.empty())
	for (CI ii=stats.begin(); ii != stats.end(); ++ii)
	  res->push_back_stat((*ii)->replace(src, rplc, symbols, out));

      return res;      
    }
    
    void push_back_decl(Code *code);
    void push_back_stat(Code *code);
    void merge(Compound *compound);

    Compound* spawn()
    {
      Compound *res = new Compound(true);
      push_back_stat(res);
      return res;
    }

    //Code *refactor();

  public:
    bool automatic;
    Compound *parent;
    std::vector<Code*> decls;    
    std::vector<Code*> stats;
};

class If : public Code
{
  public:
    If() : Code(&BasicVoid) {}
    If(Expression *_condition, Code *_data) :
      Code(&BasicVoid), condition(_condition), data(_data) {}

    ~If()
    {
      delete condition;
      delete data;
    }

    If *copy()
    {
      return new If(condition->copy(), data->copy());
    }

    Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualizeBreaks(void)
    {
      data->residualizeBreaks();
    }

    std::string toString(int indentation=0);
    std::string toTerm();

    If *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return new If(condition->replace(src, rplc, symbols, out),
		    data->replace(src, rplc, symbols, out));
    }

  public:
    Expression *condition;
    Code *data;  
};

class IfElse : public Code
{
  public:
    IfElse() : Code(&BasicVoid) {}
    IfElse(Expression *_condition, Code *_truedata, Code *_falsedata) :
      Code(&BasicVoid), condition(_condition), truedata(_truedata), falsedata(_falsedata) {}

    ~IfElse()
    {
      delete condition;
      delete truedata;
      delete falsedata;
    }

    IfElse *copy()
    {
      return new IfElse(condition->copy(), truedata->copy(), falsedata->copy());
    }

    Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualizeBreaks(void)
    {
      truedata->residualizeBreaks();
      falsedata->residualizeBreaks();
    }

    std::string toString(int indentation=0);
    std::string toTerm();

    IfElse *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return new IfElse(condition->replace(src, rplc, symbols, out),
			truedata->replace(src, rplc, symbols, out),
			falsedata->replace(src, rplc, symbols, out));
    }

  public:
    Expression *condition;
    Code *truedata, *falsedata;
};

class While : public Code
{
  public:
    While() : Code(&BasicVoid) {}
    While(Expression *_condition, Code *_data) :
      Code(&BasicVoid), condition(_condition), data(_data) {}

    ~While()
    {
      delete condition;
      delete data;
    }

    While *copy()
    {
      return new While(condition->copy(), data->copy());
    }

    Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);

    std::string toString(int indentation=0);
    std::string toTerm();

    While *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return new While(condition->replace(src, rplc, symbols, out),
		       data->replace(src, rplc, symbols, out));
    }

  public:
    Expression *condition;
    Code *data;
};

class DoWhile : public Code
{
  public:
    DoWhile() : Code(&BasicVoid) {}
    DoWhile(Code *_data, Expression *_condition) :
      Code(&BasicVoid), condition(_condition), data(_data) {}

    ~DoWhile()
    {
      delete condition;
      delete data;
    }

    DoWhile *copy()
    {
      return new DoWhile(data->copy(), condition->copy());
    }

    Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);

    std::string toString(int indentation=0);
    std::string toTerm();

    DoWhile *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return new DoWhile(data->replace(src, rplc, symbols, out),
			 condition->replace(src, rplc, symbols, out));
    }

  public:
    Expression *condition;
    Code *data;
};

class For : public Code
{
  public:
    For() : Code(&BasicVoid) {}
    For(Code *_initializer, Expression *_condition, Code *_increment, Code *_data) :
      Code(&BasicVoid), initializer(_initializer), condition(_condition),
      increment(_increment), data(_data) {}

    ~For()
    {
      delete initializer;
      delete condition;
      delete increment;
      delete data;
    }

    For *copy()
    {
      return new For(initializer->copy(), condition->copy(), increment->copy(),
		     data->copy());
    }

    Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);

    std::string toString(int indentation=0);
    std::string toTerm();

    For *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return new For(initializer->replace(src, rplc, symbols, out),
		     condition->replace(src, rplc, symbols, out),
		     increment->replace(src, rplc, symbols, out),
		     data->replace(src, rplc, symbols, out));
    }

  public:
    Code *initializer;
    Expression *condition;
    Code *increment, *data;
};

class Break : public Code
{
  public:
    Break(bool _tentative=false, bool _removed=false, Break *_generator=NULL) :
      tentative(_tentative), removed(_removed), generator(_generator) { }

    Break *copy()
    {
      return new Break(tentative, removed, generator);
    }
    
    Break* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed)
    {
      plog.reduce() << HDR << toString();

      Break *genbrk = new Break(true);
      Break *retbrk = new Break(tentative, removed, genbrk);

      out->push_back_stat(genbrk);
      return retbrk;
    }
    
    void residualizeBreaks(void)
    {
      tentative = false;
    }

    void removeGenerators(void)
    {
      if (generator)
	generator->removed = true;
    }

    std::string toString(int indentation=0)
    {
      if (tentative || removed)
	return "";
      else
        return (char*)(CStr(indent(indentation)) << "break;\n");
    }
    
    std::string toTerm()
    {
      if (tentative || removed)
	return "EmptyExp";
      else       
	return "Break";
    }

  public:
    bool tentative, removed;
    Break *generator;
};

class Return : public Code
{
  public:
    Return() : Code() {}
    Return(Type *_type, Expression *_data) : Code(_type), data(_data) {}

    ~Return()
    {
      delete data;
    }

    Return *copy()
    {
      return new Return(type, data->copy());
    }

    Break* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);

    std::string toString(int indentation=0);
    std::string toTerm();

    Return *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return new Return(type, data->replace(src, rplc, symbols, out));
    }

  public:
    Expression* data;  
};

class PpControl : public Code
{
  public:
    PpControl() : Code() {}
    PpControl(std::string _data) : Code(&Unknown), data(_data) {}

    PpControl *copy()
    {
      return new PpControl(data);
    }

    Code* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed)
    {
      out->push_back_stat(copy());
      return new VoidValue();
    }

    std::string toString(int indentation=0)
    {
      return data;
    }
    
    std::string toTerm()
    {
      CStr newdata;
      
      for (unsigned int ii=0; ii != data.length(); ++ii)
        if (data[ii] == '\"')
          newdata << "\\\"";
        else
          newdata << data[ii];
    
      return (char*)(CStr("PpControl(\"") << newdata.str().c_str() << "\")");
    }

    PpControl *replace(Code *src, Code *rplc,
		       SymbolTable *symbols, Compound *out)
    {
      return copy();
    }

  public:
    std::string data;  
};

#endif /* __CODE_H_INCLUDED */
